package com.coder4j.platform.config;


import com.alibaba.fastjson.JSON;
import com.coder4j.platform.annotation.OperationLog;
import com.coder4j.platform.entity.OperationLogEntity;
import com.coder4j.platform.enums.OperationTypeEnums;
import com.coder4j.starter.tools.enums.StatusEnums;
import com.coder4j.starter.tools.vo.R;
import com.coder4j.starter.web.util.RequestUtils;
import com.coder4j.platform.service.OperationLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ExecutorService;

/**
 * 使用 AOP 实现 Controller 日志的打印
 *
 * @author Keller
 * @date 2022-08-04 10:17:02
 */
@Aspect
@Component
@Slf4j
public class ControllerLogAspect {

    @Resource
    private OperationLogService logsService;

    @Resource
    private ExecutorService executorService;

    /**
     * 切点定在项目中所有controller层的方法上
     */
    @Pointcut("execution(* *..*Controller.*(..))")
    public void controllerLog() {
    }

    @AfterReturning(pointcut = "controllerLog()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return;
        }
        executorService.submit(() -> asyncSaveLog(joinPoint, result,requestAttributes));
    }

    public void asyncSaveLog(JoinPoint joinPoint, Object result,ServletRequestAttributes requestAttributes){
        boolean saveLogFlag = false;

        //如果方法上有@OperationLog注解，日志入库
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        OperationLog operationLog = method.getAnnotation(OperationLog.class);
        if (operationLog != null) {
            saveLogFlag = true;
        }

        //解析Request
        HttpServletRequest request = requestAttributes.getRequest();
        OperationLogEntity logEntity = new OperationLogEntity();
        logEntity.setType(OperationTypeEnums.Operation.name());
        logEntity.setName(operationLog != null && operationLog.value() != null ? operationLog.value() : method.getName());
        logEntity.setRequestMethod(request.getMethod());
        logEntity.setRequestUrl(request.getRequestURI());
        logEntity.setRequestParam(this.getRequestParam(request));
        logEntity.setStatus(StatusEnums.Success.name());

        //解析RequestBody,仅解析JSON格式的Body
        String contentType = request.getContentType();
        logEntity.setRequestBody(this.getRequestBody(contentType, joinPoint));

        log.info("===>Request,url:{},params:{},body{}", logEntity.getRequestUrl(), logEntity.getRequestParam(), logEntity.getRequestBody());

        //解析Response
        R r = R.error();
        if(result != null){
            if(result instanceof String){
                r = R.success(result);
            }else {
                String json = JSON.toJSONString(result);
                logEntity.setResponseParam(json);
                r = JSON.parseObject(json, R.class);
            }
        }
        log.info("===>Response,{}", logEntity.getResponseParam());

        //如果方法上有@OperationLog注解，或者Response不是success，都记录操作日志
        if (saveLogFlag || !r.isSuccess()) {
            //读取IP、浏览器、操作系统参数
            logEntity.setIp(RequestUtils.getIpAddress(request));
            /**
             * User-Agent:
             * Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
             */
//            logEntity.setAddress(CommonIpAddressUtil.getCityInfo(logEntity.getIp()));
            logEntity.setBrowser(RequestUtils.getBrowser(request));
            logEntity.setOs(RequestUtils.getOS(request));
            logEntity.setUserAgent(RequestUtils.getUserAgent(request));
            //获取登录用户信息
            try {
//                SaBaseLoginUser loginUser = AuthUtil.getLoginUser();
//                if (ObjectUtil.isNotNull(loginUser)) {
//                    logEntity.setCreateName(loginUser.getName());
//                    logEntity.setCreateId(Long.parseLong(loginUser.getId()));
//                }
            } catch (Exception ignored) {
            }
            //如果应答不是success，记录操作状态为失败
            if (!r.isSuccess()) {
                logEntity.setStatus(StatusEnums.Failed.name());
            }
            logsService.baseSave(logEntity);
        }
    }


    /**
     * 获取请求体
     *
     * @param contentType
     * @param joinPoint
     * @return
     */
    private String getRequestBody(String contentType, JoinPoint joinPoint) {
        try {
            Object[] args = joinPoint.getArgs();
            if (args == null) {
                return null;
            }
            for (Object arg : args) {
                if (arg instanceof HttpServletRequest
                        || arg instanceof HttpServletResponse
                        || arg instanceof Exception
                ) {
                    continue;
                }
//                if(arg instanceof Exception){
//                    Exception e =  (Exception) arg;
//                    return e.getMessage();
//                }

                //打印json参数
                if (contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) {
                    return JSON.toJSONString(arg);
                }
            }
            return null;
        }catch (Exception e){

        }
        return null;
    }

    /**
     * 获取请求参数
     *
     * @param request
     * @return
     */
    private String getRequestParam(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (parameterMap.isEmpty()) {
            return null;
        }
        StringBuilder param = new StringBuilder();
        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            param.append(entry.getKey()).append(":").append(Arrays.toString(entry.getValue())).append("\n");
        }
        return param.toString();
    }

}
